package com.example.job.service;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.common.service.ServiceImpl;
import com.example.job.mapper.SysJobMapper;
import com.example.job.po.SysJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Service;
import org.quartz.*;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.support.CronSequenceGenerator;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;

@Slf4j
@Service
public class SysJobServiceImpl extends ServiceImpl<SysJob> implements SysJobService, ApplicationContextAware {

    public static final String JOB_GROUP_KEY = "com.example.job.group.key";

    private ApplicationContext applicationContext;

    @Autowired
    private Scheduler scheduler;

    @Autowired
    private SysJobMapper sysJobMapper;


    @Override
    public void addJob(SysJob job) {
        try {
            Class clz = Class.forName(job.getClassName());
            Date nextTime = job.getNextTime();
            Object bean = applicationContext.getBean(clz);
            String jobName = bean.getClass().getName();
            JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) bean.getClass()).withIdentity(jobName, JOB_GROUP_KEY).build();
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCron()).withMisfireHandlingInstructionDoNothing();
            if (nextTime == null || nextTime.getTime() < new Date().getTime()) {
                nextTime = getNextTime(new Date(), job.getCron());
            }
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, JOB_GROUP_KEY).startAt(nextTime)
                    .withSchedule(scheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (Exception e) {
            log.error("======start job error =========>" + job.toString());
        }
    }

    @Override
    public String getJobState(SysJob job) throws Exception {
        Class clz = Class.forName(job.getClassName());
        Object bean = applicationContext.getBean(clz);
        String jobName = bean.getClass().getName();
        TriggerKey triggerKey = new TriggerKey(jobName, JOB_GROUP_KEY);
        return scheduler.getTriggerState(triggerKey).name();
    }

    @Override
    public void pauseJob(SysJob job) throws Exception {
        Class clz = Class.forName(job.getClassName());
        Object bean = applicationContext.getBean(clz);
        String jobName = bean.getClass().getName();
        JobKey jobKey = new JobKey(jobName, JOB_GROUP_KEY);
        scheduler.pauseJob(jobKey);
    }

    @Override
    public void resumeJob(SysJob job) throws Exception {
        Class clz = Class.forName(job.getClassName());
        Object bean = applicationContext.getBean(clz);
        String jobName = bean.getClass().getName();
        JobKey jobKey = new JobKey(jobName, JOB_GROUP_KEY);
        scheduler.resumeJob(jobKey);
    }

    @Override
    public void deleteJob(SysJob job) throws Exception {
        Class clz = Class.forName(job.getClassName());
        Object bean = applicationContext.getBean(clz);
        String jobName = bean.getClass().getName();
        JobKey jobKey = new JobKey(jobName, JOB_GROUP_KEY);
        scheduler.deleteJob(jobKey);
    }

    @Override
    public void modifyJob(SysJob job) throws Exception {
        Class clz = Class.forName(job.getClassName());
        Object bean = applicationContext.getBean(clz);
        String jobName = bean.getClass().getName();
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, JOB_GROUP_KEY);
        JobKey jobKey = new JobKey(jobName, JOB_GROUP_KEY);
        if (scheduler.checkExists(jobKey) && scheduler.checkExists(triggerKey)) {
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCron()).withMisfireHandlingInstructionDoNothing();
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                    .withSchedule(scheduleBuilder).build();
            scheduler.rescheduleJob(triggerKey, trigger);
        }
    }

    @Override
    public Date getNextTime(Date startTime, String cron) throws Exception {
        CronSequenceGenerator cronSequenceGenerator = new CronSequenceGenerator(cron);
        return cronSequenceGenerator.next(startTime);
    }

    @Override
    public void initJobService() throws Exception {
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(com.example.job.annotation.Job.class);
        for (Map.Entry<String, Object> entry : beansWithAnnotation.entrySet()) {
            Object bean = entry.getValue();
            com.example.job.annotation.Job xxlJob = bean.getClass().getAnnotation(com.example.job.annotation.Job.class);
            SysJob sysJob = null;
            if (bean instanceof Job) {
                sysJob = sysJobMapper.getJobByBeanClassName(bean.getClass().getName());
                if (sysJob == null) {
                    sysJob = new SysJob();
                    sysJob.setName(xxlJob.name());
                    sysJob.setAvailable(xxlJob.available());
                    sysJob.setClassName(bean.getClass().getName());
                    sysJob.setCron(xxlJob.cron());
                    sysJobMapper.insert(sysJob);
                    if (xxlJob.available()) {
                        addJob(sysJob);
                    }
                } else {
                    if (xxlJob.available()) {
                        addJob(sysJob);
                    }
                }
            }
        }
    }

    @Override
    public String createCronExpression(Date date) throws Exception {
        StringBuffer cronExpression = new StringBuffer();
        java.util.Calendar calendar = java.util.Calendar.getInstance();
        calendar.setTime(date);
        cronExpression.append("0 ").append(calendar.get(java.util.Calendar.MINUTE) + " ")
                .append(calendar.get(java.util.Calendar.HOUR_OF_DAY) + " ").append(calendar.get(java.util.Calendar.DATE) + " ")
                .append((calendar.get(java.util.Calendar.MONTH) + 1) + " ").append("? " + calendar.get(Calendar.YEAR));
        return cronExpression.toString();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public BaseMapper<SysJob> getMapper() {
        return sysJobMapper;
    }
}